package top.mysql.SQL2;

/**
 *          SQL 优化
 *
 */
public class SqlOptimization {

/**
 *      --------------------------总结---------------------
 *
 *      插入数据：
 *          insert: 批量插入  手动控制事务  主键顺序插入
 *          大批量插入：load data local infile
 *      主键优化：
 *          主键长度尽量短  顺序插入
 *      order by优化：
 *          using index：直接通过索引返回的数据，性能高
 *          using filesort：需要将返回的结果在排序缓冲区排序
 *      group by优化：
 *          索引，多字段分组满足最左前缀法则
 *      limit优化：
 *          覆盖索引 + 子查询
 *      count优化：
 *          count(*) ≈ count(1) > count(主键 id) > count(字段)，所以尽量使用 count(*)
 *      update优化：
 *          尽量根据主键/索引字段进行数据更新
 */

/**
 *
 *      --------------------------插入优化---------------------
 *
 *      如果我们需要一次性往数据库表中插入多条记录，可以从以下三个方面进行优化。
 *
 *      优化方案一：       批量插入数据(每次Insert都需要与数据库建立连接进行网络传输)
 *      Insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
 *
 *      优化方案二：      手动控制事务(mysql默认开启自动事务提交，执行完insert语句后频繁的开启关闭事务和提交)
 *      start transaction;
 *      insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
 *      insert into tb_test values(4,'Tom'),(5,'Cat'),(6,'Jerry');
 *      insert into tb_test values(7,'Tom'),(8,'Cat'),(9,'Jerry');
 *      commit;
 *
 *      优化方案三：     主键顺序插入，性能要高于乱序插入。
 *      主键乱序插入 : 8 1 9 21 88 2 4 15 89 5 7 3
 *      主键顺序插入 : 1 2 3 4 5 7 8 9 15 21 88 89
 *
 *
 *      大批量插入数据
 *      如果一次性需要插入大批量数据(比如: 几百万的记录)，使用insert语句插入性能较低，此时可以使用MySQL数据库
 *      提供的load指令进行插入。操作如下：
 *      -- 客户端连接服务端时，加上参数 -–local-infile
 *      mysql –-local-infile -u root -p
 *      -- 设置全局参数local_infile为1，开启从本地加载文件导入数据的开关
 *      set global local_infile = 1;
 *      -- 执行load指令将准备好的数据，加载到表结构中
 *      load data local infile '/root/sql1.log' into table tb_user fields
 *      terminated by ',' lines terminated by '\n' ;
 *
 *      --------------------------主键优化---------------------
 *
 *      在InnoDB存储引擎中，表数据都是根据主键顺序组织存放的，这种存储方式的表称为索引组织表
 *
 *      InnoDB的逻辑存储结构   表空间 -> 段 -> 区(1M) -> 页(16K) -> 行
 *
 *      页分裂：
 *      页可以为空，也可以填充一半，也可以填充100%。每个页包含了2-N行数据(如果一行数据过大，会行溢出)，根据主键排列。
 *      主键乱插入的情况下可能会发生页分裂现象，"页分裂"，是比较耗费性能的操作。
 *
 *      页合并：
 *      当删除一行记录时，实际上记录并没有被物理删除，只是记录被标记（flaged）为删除并且它的空间变得允许被其他记录
 *      声明使用。当我们继续删除该页的数据，当页中删除的记录达到 MERGE_THRESHOLD（默认为页的50%），InnoDB会开
 *      始寻找最靠近的页（前或后）看看是否可以将两个页合并以优化空间使用。
 *
 *      注意：
 *      MERGE_THRESHOLD：合并页的阈值，可以自己设置，在创建表或者创建索引时指定。
 *
 *      索引设计原则：
 *      满足业务需求的情况下，尽量降低主键的长度。
 *      插入数据时，尽量选择顺序插入，选择使用AUTO_INCREMENT自增主键。
 *      尽量不要使用UUID做主键或者是其他自然主键，如身份证号。
 *      业务操作时，避免对主键的修改。
 *
 *      --------------------------order by优化---------------------
 *
 *      MySQL的排序，有两种方式：
 *      Using filesort：通过表索引或全表扫描，读取满足条件的数据行，然后在排序缓冲区sortbuffer中完成排序操作，
 *                      所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
 *      Using index：通过有序索引顺序扫描直接返回有序数据，这种情况即为 using index，不需要额外排序，操作效率高
 *
 *      对于以上的两种排序方式，Using index的性能高，而Using filesort的性能低，我们在优化排序操作时，
 *      尽量要优化为 Using index。(用explain方式的Extra查看)
 *
 *      创建 age,phone 的联合索引
 *       按 order by phone，age 排序会出现 Using index，Using filesort    原因是违背最左前缀法则
 *       按 order by age asc,phone desc 排序会出现 Using index，Using filesort 创建的索引是Asc
 *          在创建时指定索引的排序方式可以优化
 *          create index idx_user_age_phone_ad on tb_user(age asc ,phone desc);
 *
 *      order by优化原则:
 *          根据排序字段建立合适的索引，多字段排序时，也遵循最左前缀法则。
 *          order by优化前提条件是使用覆盖索引。
 *          多字段排序, 一个升序一个降序，此时需要注意联合索引在创建时的规则（ASC/DESC）。
 *          如果不可避免的出现filesort，大数据量排序时，可以适当增大排序缓冲区大小sort_buffer_size(默认256k)。
 *
 *
 *      --------------------------group by优化---------------------
 *
 *      针对于 profession ， age， status 创建一个联合索引。
 *
 *      如果仅仅根据age分组，就会出现 Using temporary ；而如果是 根据profession,age两个字段同时分组，
 *      则不会出现 Using temporary。原因是因为对于分组操作，在联合索引中，也是符合最左前缀法则的。
 *
 *      select age,count(*), from tb_user where profession='软件工程' group by age;
 *      也是 Using index ，因为符合最左前缀法则
 *
 *      所以，在分组操作中，我们需要通过以下两点进行优化，以提升性能：
 *          在分组操作时，可以通过索引来提高效率。
 *          分组操作时，索引的使用也是满足最左前缀法则的。
 *
 *      --------------------------limit优化---------------------
 *
 *      在数据量比较大时，如果进行limit分页查询，在查询时，越往后，分页查询效率越低。
 *
 *      优化思路:
 *      一般分页查询时，通过创建 覆盖索引 能够比较好地提高性能，可以通过覆盖索引加子查询形式进行优化。
 *
 *      把查询出来的数据当成一张表：
 *      explain select * from tb_sku t , (select id from tb_sku order by idlimit 2000000,10)
 *      a where t.id = a.id;
 *
 *      --------------------------count优化---------------------
 *
 *      如果说要大幅度提升InnoDB表的count效率，主要的优化思路：自己计数(可以借助于redis这样的数据库进行,
 *      但是如果是带条件的count又比较麻烦了)。
 *
 *      count() 是一个聚合函数，对于返回的结果集，一行行地判断，如果 count 函数的参数不是NULL，累计值就加 1，
 *      否则不加，最后返回累计值。
 *
 *      count用法                                     含义
 *      count(主键)           InnoDB 引擎会遍历整张表，把每一行的 主键id 值都取出来，返回给服务层。
 *                           服务层拿到主键后，直接按行进行累加(主键不可能为null)
 *
 *      count(字段)           没有not null 约束 : InnoDB 引擎会遍历整张表把每一行的字段值都取出来，
 *                          返回给服务层，服务层判断是否为null，不为null，计数累加。有not null约束：InnoDB
 *                          引擎会遍历整张表把每一行的字段值都取出来，返回给服务层，直接按行进行累加。
 *
 *      count(数字)           InnoDB 引擎遍历整张表，但不取值。服务层对于返回的每一行，放一个数字“1”进去，
 *                          直接按行进行累加。
 *
 *      count(*)            InnoDB引擎并不会把全部字段取出来，而是专门做了优化，不取值，服务层直接按行进行累加。
 *
 *      按照效率排序的话，count(字段) < count(主键 id) < count(1) ≈ count(*)，所以尽量使用 count(*)。
 *
 *
 *      --------------------------update优化---------------------
 *
 *      update course set name = 'SpringBoot' where name = 'PHP' ;
 *      当我们开启多个事务，在执行SQL时，我们发现行锁升级为了表锁。
 *
 *      update course set name = 'javaEE' where id = 1 ;
 *      执行这条语句时不会导致行锁升级为表锁
 *
 *      更新的字段建立索引，规避行锁升级为表锁的问题
 *
 *      InnoDB的行锁是针对索引加的锁，不是针对记录加的锁 ,并且该索引不能失效，否则会从行锁升级为表锁 。
 *
 */
}
